#include"DGGSFeatureLayer.hpp"
#include"DGGSFeatureClass.hpp"
#include"DGGSFeature.hpp"
#include"DGGSPoint.hpp"
#include"DGGSPolyLine.hpp"
#include"DGGSPolygon.hpp"
#include"../LibDGGSKernel/IDGGSKernel.hpp"
#include"../LibDGGSBase/MathDefine.hpp"
#include"../3rdlibs/include/json/json.h"

#include<vector>
#include<iostream>
#include<fstream>
#include<string>

using namespace NNU::DGGS;
using namespace NNU::DGGS::Kernel;
using namespace NNU::DGGS::Feature;

DGGSFeatureLayer::DGGSFeatureLayer()
{
    this->_featureClass= nullptr;

    //分配空间用于存储feature，空容器
    this->_features=new std::vector<DGGSFeature*>();
}

DGGSFeatureLayer::~DGGSFeatureLayer()
{
    delete this->_featureClass;
    this->_featureClass= nullptr;
    delete this->_features;
    DestroyKernelObj(this->pKernel);
}

DGGSFeatureLayer::DGGSFeatureLayer(DGGSFeatureClass &featureclass)
{
    int level=featureclass.getLevel();
    DGGSGeometryType geometryType=featureclass.getGeometryType();
    DGGSGridType gridType=featureclass.getGridType();
    DGGSElementType elementType=featureclass.getElementType();
    this->_featureClass=new DGGSFeatureClass(level,geometryType,gridType,elementType);

    this->pKernel= CreateKernelObj(gridType);
    //分配空间用于存储feature，空容器
    this->_features=new std::vector<DGGSFeature*>();
}

int DGGSFeatureLayer::getLevel()
{
    if(this->_featureClass)
        return this->_featureClass->getLevel();
    else
        return -1;
}

DGGSFeature* DGGSFeatureLayer::getFeature(int featureIndex)
{
    if(this->_features!= nullptr)
    {
        int featureCount = this->getFeatureCount();
        for(int i=0;i<featureCount;i++)
        {
            if((*this->_features)[i]->getFID()==featureIndex)
                return (*this->_features)[i];
        }
        return nullptr;
    }
    else
        return nullptr;
}

bool DGGSFeatureLayer::addFeature(DGGSFeature *feature)
{
    if(this->_features!= nullptr)
    {
        auto fId=feature->getFID();
        if(this->getFeature((int)fId)== nullptr)
        {
            if(this->_features!= nullptr)
                this->_features->push_back(feature);
            return true;
        }
        else
        {
            return false;
        }

    }
    else
        return false;
}

bool DGGSFeatureLayer::eraseFeature(DGGSFeature *feature)
{
    if(this->_features != nullptr)
    {
        int eraseId=0;
        for(int i=0;i<this->_features->size();i++)
            if(&(*this->_features)[i]==&feature)
            {
                eraseId=i;
                break;
            }
        if(eraseId<this->_features->size())
        {
            this->_features->erase(this->_features->begin() + eraseId);
            return true;
        }
        else
            return false;
    }
    else
        return false;

}

int DGGSFeatureLayer::getFeatureCount()
{
    if(this->_features== nullptr)
        return -1;
    else
        return (int)this->_features->size();
}

// code转为json对象
void DGGSFeatureLayer::code2JsonGeo(DGGSElementCode code,long long int featureId,Json::Value &feature)
{
    //屬性
    Json::Value properties;
    properties["code"]=Json::Value((std::to_string(code.basePartition)+" "+std::to_string(code.row)+" "+std::to_string(code.col)).c_str());
    if(code.level!=this->getLevel())
        properties["id"] = Json::Value(-1*code2Id(code,this->getFeatureClass()->getGridType()));
    else
        properties["id"] = Json::Value(code2Id(code,this->getFeatureClass()->getGridType()));
    properties["FeatureId"] = Json::Value(featureId);
    if(code.level!=this->getLevel()&&this->getFeatureClass()->getGeometryType()==DGGSGeometryType::ST_Polygon)
        properties["ringEnd"] = Json::Value(1);
    else if(this->getFeatureClass()->getGeometryType()==DGGSGeometryType::ST_Polygon)
        properties["ringEnd"] = Json::Value(0);
    feature["properties"]=properties;

    code.level=this->getLevel();

    //几何
    switch (code.elementType) {
        case DGGSElementType::Vertex:
        {
            GeoCoord coord{};
            this->pKernel->decode(code, coord);
            Json::Value geometry;
            geometry["type"] = "point";
            geometry["coordinates"].append(Json::Value((double)coord.lon));
            geometry["coordinates"].append(Json::Value((double)coord.lat));
            geometry["coordinates"].append(Json::Value(0));
            feature["geometry"] = geometry;
            feature["type"] = "Feature";
            break;
        }
        case DGGSElementType::Edge:
        {
            DGGSElementCodeIter*iter= nullptr;
            int vertexesNum;
            pKernel->query(DGGSTopoType::RelativeVertexes, code,iter,vertexesNum);
            Json::Value geometry;
            Json::Value point;
            for (int i = 0; i < 2; i++)
            {
                GeoCoord coord{};
                pKernel->decode(iter[i].code,coord);
                point["point"].append(Json::Value((double)coord.lon));
                point["point"].append(Json::Value((double)coord.lat));
                point["point"].append(Json::Value(0));
                point["coordinate"].append(point["point"]);
                point["point"].clear();
            }
            delete[]iter;
            geometry["coordinates"].append(point["coordinate"]);
            geometry["type"] = "LineString";
            feature["geometry"] = geometry;
            feature["type"] = "Feature";
            break;
        }
        case DGGSElementType::Cell:
        {
            DGGSElementCodeIter*iter= nullptr;
            int vertexesNum;
            pKernel->query(DGGSTopoType::RelativeVertexes, code,iter,vertexesNum);
            Json::Value geometry;
            Json::Value point;
            for (int i = 0; i < vertexesNum; i++)
            {
                GeoCoord coord{};
                pKernel->decode(iter[i].code,coord);
                point["point"].append(Json::Value((double)coord.lon));
                point["point"].append(Json::Value((double)coord.lat));
                point["point"].append(Json::Value(0));
                point["coordinate"].append(point["point"]);
                point["point"].clear();
            }
            delete[]iter;
            geometry["coordinates"].append(point["coordinate"]);
            geometry["type"] = "Polygon";
            feature["geometry"] = geometry;
            feature["properties"]=properties;
            feature["type"] = "Feature";
            break;
        }
        default:
            break;
    }

}

//保存到文件
void DGGSFeatureLayer::saveToFile(const char *fileName)
{
    if(this->_featureClass== nullptr)
        return;
    //新版JSONCPP有些变化，需要调用它的writer来写入文件
    std::ofstream jsonFile;
    jsonFile.open(std::string(fileName));
    Json::StreamWriterBuilder builder;
    const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());

    //layer基本参数写入json
    Json::Value root;
    root["name"] = "featureLayer";
    root["level"] = Json::Value(this->getLevel());
    root["geometryType"] = Json::Value(this->_featureClass->getGeometryType());
    root["gridType"] = Json::Value((int) this->_featureClass->getGridType());
    root["elementType"] = Json::Value((int) this->_featureClass->getElementType());
    root["featureCount"] = Json::Value(this->getFeatureCount());

    root["type"] = "FeatureCollection";
    root["tag"] = "NNUDGGSFileFormat";
    root["version"] = Json::Value(1.0);

    //features写入json文件
    for(int i=0;i<this->getFeatureCount();i++)
    {
        Json::Value jsonFeature;
        DGGSFeature *feature=this->getFeature(i);

        //写入FID
        long long int featureId=feature->getFID();

        //写入geometry
        DGGSElementCode* codes= nullptr;
        feature->getGeometry()->getGridData(codes);
        int codeCount=feature->getGeometry()->getGridCount();
        for(int c=0;c<codeCount;c++)
        {
            code2JsonGeo( codes[c],featureId,jsonFeature);
            root["features"].append(jsonFeature);
        }
    }
    
    //关闭文件
    writer->write(root, &jsonFile);
    root["features"].clear();
    jsonFile.close();

}

//从文件中加载
bool DGGSFeatureLayer::loadFromFile(const char* fileName)
{
    //读取json文件，设置各个部分的包围盒
    std::ifstream jsonFile(fileName);
    if (!jsonFile.is_open()) {
        std::cerr << "Failed to open file." << std::endl;
        return false;
    }
    // 创建 Json::Value 对象来保存解析后的 JSON 数据
    Json::Value root;
    Json::CharReaderBuilder builder;
    std::string errs;
    if (!parseFromStream(builder, jsonFile, &root, &errs)) {
        std::cerr << "Failed to parse JSON: " << errs << std::endl;
        return false;
    }

    //设置featureClass
    int level=root["level"].asInt();
    auto elementType =static_cast<DGGSElementType> (root["elementType"].asInt());
    auto gridType = static_cast<DGGSGridType>(root["gridType"].asInt());
    auto geometryType=static_cast<DGGSGeometryType>(root["geometryType"].asInt());
    this->_featureClass=new DGGSFeatureClass(level,geometryType,gridType,elementType);
    this->pKernel= CreateKernelObj(gridType);

    int featureCount=root["featureCount"].asInt();

    int codeSumCount=(int)root["features"].size();
    int codeId=0;
    //设置feature
    for(int i=0;i<featureCount;i++)
    {
        //获取fid
        DGGSFeature *feature=this->_featureClass->createFeature();
        int fId=root["features"][codeId]["properties"]["FeatureId"].asInt();

        //获取geometry
        std::vector<DGGSElementCode> tempCodes;
        int codeCount=0;
        while (fId==root["features"][codeId]["properties"]["FeatureId"].asInt()&&codeId<codeSumCount)
        {
            DGGSElementCode code;
            code.level=level;
            code.elementType=elementType;
            auto id=(long long int)root["features"][codeId]["properties"]["id"].asInt64();
            id2Code(abs(id),code,this->getFeatureClass()->getGridType());
            if(id<0)
                code.level-=1;
            tempCodes.push_back(code);
            codeId++;
            codeCount++;
        }
        auto codes=new DGGSElementCode[codeCount];
        for(int c=0;c<codeCount;c++)
            if(tempCodes[c].level==this->getLevel())
                codes[c]=tempCodes[c];

        feature->setFID(fId);

        //创建feature
        switch (geometryType)
        {
            case DGGSGeometryType::ST_Point:
            {
                DGGSPoint point;
                point.setGridCount(codeCount);
                point.setGridData(codes);
                feature->setGeometry(&point);
                break;
            }
            case DGGSGeometryType::ST_PolyLine:
            {
                DGGSPolyLine line;
                line.setGridCount(codeCount);
                line.setGridData(codes);
                feature->setGeometry(&line);
                break;
            }
            case DGGSGeometryType::ST_Polygon:
            {
                DGGSPolygon polygon;
                polygon.setGridCount(codeCount);
                polygon.setGridData(codes);
                feature->setGeometry(&polygon);
                break;
            }
            default:
                break;
        }

        this->addFeature(feature);
        delete[]codes;
    }

    root["features"].clear();
    root.clear();
    jsonFile.close();
    return true;
}




